function CharacterControls(model, mixer, animationsMap, orbitControl, camera) {
	const startAction = 'idle'
	this.walkDirection = new THREE.Vector3()
	this.rotateAngle = new THREE.Vector3(0, 1, 0)
	this.rotateQuarternion = new THREE.Quaternion()
	this.cameraTarget = new THREE.Vector3()
	this.fadeDuration = 0.2
	this.walkVelocity = 2

	this.model = model
	this.mixer = mixer
	this.animationsMap = animationsMap
	for (const key in this.animationsMap) {
		if (key == startAction) {
			this.animationsMap[key].play()
		}
	}
	this.currentAction = startAction
	this.orbitControl = orbitControl
	this.camera = camera
	this.updateCameraTarget(0, 0)
}

CharacterControls.prototype.updateCameraTarget = function (moveX, moveZ) {
	this.camera.position.x += moveX
	this.camera.position.z += moveZ

	this.cameraTarget.x = this.model.position.x
	this.cameraTarget.y = this.model.position.y + 1
	this.cameraTarget.z = this.model.position.z
	this.orbitControl.target = this.cameraTarget
}

CharacterControls.prototype.update = function (delta, keysPressed) {
	const directionPressed = ['w', 'a', 's', 'd'].some(key => keysPressed[key] == true)

	let play = '';
	if (directionPressed) {
		play = 'walk'
	} else {
		play = 'idle'
	}

	if (this.currentAction != play) {
		for (const key in this.animationsMap) {
			if (key === play) {
				const toPlayAction = this.animationsMap[key]
				const current = this.animationsMap[this.currentAction]
				current.fadeOut(this.fadeDuration)
				toPlayAction.reset().fadeIn(this.fadeDuration).play()
				this.currentAction = play
			}
		}
	}

	this.mixer.update(delta)

	if (this.currentAction == 'walk') {
		// calculate towards camera direction
		const angleYCameraDirection = Math.atan2(
			(this.camera.position.x - this.model.position.x),
			(this.camera.position.z - this.model.position.z))
		const directionOffset = this.directionOffset(keysPressed)
		// rotate model
		this.rotateQuarternion.setFromAxisAngle(this.rotateAngle, angleYCameraDirection + directionOffset)
		this.model.quaternion.rotateTowards(this.rotateQuarternion, 0.2)
		// calculate direction
		this.camera.getWorldDirection(this.walkDirection)
		this.walkDirection.y = 0
		this.walkDirection.normalize()
		this.walkDirection.applyAxisAngle(this.rotateAngle, directionOffset)
		// walk velocity
		const velocity = this.walkVelocity

		// move model & camera
		const moveX = -this.walkDirection.x * velocity * delta
		const moveZ = -this.walkDirection.z * velocity * delta
		this.model.position.x += moveX
		this.model.position.z += moveZ
		this.updateCameraTarget(moveX, moveZ)
	}
}

CharacterControls.prototype.directionOffset = function (keysPressed) {
	var directionOffset = 0 // w

	if (keysPressed.w) {
		if (keysPressed.a) {
			directionOffset = Math.PI / 4 - Math.PI // w+a
		} else if (keysPressed.d) {
			directionOffset = - Math.PI / 4 + Math.PI // w+d
		} else {
			directionOffset = Math.PI // w
		}
	} else if (keysPressed.s) {
		if (keysPressed.a) {
			directionOffset = Math.PI / 4 - Math.PI / 2 // s+a
		} else if (keysPressed.d) {
			directionOffset = -Math.PI / 4 + Math.PI / 2 // s+d
		}
	} else if (keysPressed.a) {
		directionOffset = -Math.PI / 2 // a
	} else if (keysPressed.d) {
		directionOffset = Math.PI / 2 // d
	}

	return directionOffset
}